using Microsoft.SqlServer.Management.Sdk.Sfc;
using Microsoft.SqlServer.Tools.HostingServices;
using Microsoft.SqlServer.Tools.PublishWizard.Helpers;
using System;
using System.Data;
using System.Data.SqlClient;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Net;
using System.Text;
using System.Xml.Serialization;

namespace Microsoft.SqlServer.Management.SqlManagerUI.Scripting
{
	internal class PublishToWebWriterProvider : TextWriterProvider, IObjectsSupported
	{
		private enum PublishingState
		{
			NotStarted,
			Publishing,
			Completed,
			Error
		}

		private enum ComputingRowCountState
		{
			IncreasingRows,
			DecreasingRows,
			RowsFound
		}

		private const int _growthConstant = 10;

		private const ulong _MaxSizeTillRowsAreIncreasedInBytes = 10485760uL;

		private PublishToWebWriterProvider.PublishingState _publishingState;

		private PublishService _webService;

		private WebServicePublishOptions _webServiceOptions;

		private string _webServiceSqlServerName;

		private string _webServiceSqlDatabaseName;

		private string _webServiceSqlUserName;

		private string _webServiceSqlPassword;

		private bool _useTransactions = true;

		private StringBuilder _currentScript = new StringBuilder();

		private string _batchSeperator = string.Empty;

		private bool _useBatchSeperator;

		public WebServicePublishOptions WebServiceOptions
		{
			get
			{
				return this._webServiceOptions;
			}
		}

		public PublishToWebWriterProvider(string webServiceAddress, string webServiceUserName, string webServicePassword, string webServiceSqlServerName, string webServiceSqlDatabaseName, string webServiceSqlUserName, string webServiceSqlPassword, string batchSeperator, bool useTransactions)
		{
			TraceHelpers.TraceEnter(Logging.ScriptingEngineSwitch);
			ArgumentValidationHelpers.ValidateStringNotNullOrEmpty(webServiceAddress, "webServiceAddress");
			ArgumentValidationHelpers.ValidateStringNotNullOrEmpty(webServiceUserName, "webServiceUserName");
			ArgumentValidationHelpers.ValidateStringNotNullOrEmpty(webServicePassword, "webServicePassword");
			ArgumentValidationHelpers.ValidateStringNotNullOrEmpty(webServiceSqlServerName, "webServiceSqlServerName");
			ArgumentValidationHelpers.ValidateStringNotNullOrEmpty(webServiceSqlDatabaseName, "webServiceSqlDatabaseName");
			ArgumentValidationHelpers.ValidateStringNotNullOrEmpty(webServiceSqlUserName, "webServiceSqlUserName");
			ArgumentValidationHelpers.ValidateStringNotNullOrEmpty(webServiceSqlPassword, "webServiceSqlPassword");
			this._webServiceSqlServerName = webServiceSqlServerName;
			this._webServiceSqlDatabaseName = webServiceSqlDatabaseName;
			this._webServiceSqlUserName = webServiceSqlUserName;
			this._webServiceSqlPassword = webServiceSqlPassword;
			this._useTransactions = useTransactions;
			this._batchSeperator = batchSeperator.Trim();
			this._useBatchSeperator = !string.IsNullOrEmpty(batchSeperator);
			this._webService = new PublishService();
			this._webService.Url = webServiceAddress;
			this._webService.Credentials = new NetworkCredential(webServiceUserName, webServicePassword);
			this._webService.CookieContainer = new CookieContainer();
			this._webService.Timeout = 2147483647;
			this._webServiceOptions = new WebServicePublishOptions(this._webService, this._webServiceSqlServerName, this._webServiceSqlDatabaseName, this._webServiceSqlUserName, this._webServiceSqlPassword);
		}

		public override void InitializeForUrn(Urn item)
		{
			TraceHelpers.TraceEnter(Logging.ScriptingEngineSwitch);
			if (this._publishingState == PublishToWebWriterProvider.PublishingState.NotStarted)
			{
				this._webService.BeginPublish(this._webServiceSqlServerName, this._webServiceSqlDatabaseName, this._webServiceSqlUserName, this._webServiceSqlPassword, this._useTransactions);
				this._publishingState = PublishToWebWriterProvider.PublishingState.Publishing;
			}
		}

		public override void DisposeForUrn()
		{
		}

		public override void Dispose()
		{
			TraceHelpers.TraceEnter(Logging.ScriptingEngineSwitch);
			base.Dispose();
			if (this._publishingState == PublishToWebWriterProvider.PublishingState.Publishing)
			{
				this.PublishCachedScript();
				this._webService.EndPublish();
				this._publishingState = PublishToWebWriterProvider.PublishingState.Completed;
			}
			else if (this._publishingState == PublishToWebWriterProvider.PublishingState.Error && this._webService != null)
			{
				this._webService.CancelPublish();
			}
			if (this._webService != null)
			{
				this._webService.Dispose();
				this._webService = null;
			}
		}

		public override void WriteLine(string script)
		{
			try
			{
				TraceHelpers.TraceEnter(Logging.ScriptingEngineSwitch);
				if (!this.IsSizeAllowed((ulong)((long)script.Length * 2L)))
				{
					throw new PublishingServiceException(string.Format(CultureInfo.CurrentCulture, ScriptingEngineResources.ERROR_ScriptLongerThanMaxAllowedSize, new object[]
					{
						script
					}));
				}
				if (this._useBatchSeperator)
				{
					if (script.Trim().Equals(this._batchSeperator, StringComparison.OrdinalIgnoreCase))
					{
						this.PublishCachedScript();
					}
					else
					{
						if (!this.IsSizeAllowed((ulong)(((long)this._currentScript.Length + (long)script.Length) * 2L)))
						{
							this.PublishCachedScript();
						}
						this._currentScript.AppendLine(script);
					}
				}
				else
				{
					this.PublishScript(script);
				}
			}
			catch (Exception)
			{
				this._publishingState = PublishToWebWriterProvider.PublishingState.Error;
				throw;
			}
		}

		private void PublishCachedScript()
		{
			TraceHelpers.TraceEnter(Logging.ScriptingEngineSwitch);
			string text = this._currentScript.ToString();
			this._currentScript = new StringBuilder();
			if (!string.IsNullOrEmpty(text))
			{
				this.PublishScript(text);
			}
		}

		public override TextWriter GetTextWriter(Urn item)
		{
			TraceHelpers.TraceEnter(Logging.ScriptingEngineSwitch);
			throw new NotSupportedException();
		}

		public bool IObjectsSupported(object obj)
		{
			TraceHelpers.TraceEnter(Logging.ScriptingEngineSwitch);
			bool flag = false;
			if (obj is TableDataWrapper)
			{
				TableDataWrapper tableDataWrapper = obj as TableDataWrapper;
				flag = tableDataWrapper.HasUserDefinedType;
			}
			return typeof(TableDataWrapper).IsAssignableFrom(obj.GetType()) & !flag;
		}

		public void WriteObject(object obj)
		{
			try
			{
				TraceHelpers.TraceEnter(Logging.ScriptingEngineSwitch);
				if (!this.IObjectsSupported(obj))
				{
					throw new NotSupportedException();
				}
				this.PublishCachedScript();
				TableDataWrapper tableDataWrapper = obj as TableDataWrapper;
				if (tableDataWrapper.HasWritableColumns)
				{
					Stopwatch stopwatch = new Stopwatch();
					stopwatch.Start();
					using (SqlDataAdapter dataAdapter = tableDataWrapper.GetDataAdapter())
					{
						XmlSerializer dataSetSerializer = new XmlSerializer(typeof(DataSet));
						int num = 0;
						Stopwatch stopwatch2 = new Stopwatch();
						stopwatch2.Start();
						int num2 = 1;
						DataSet dataSet = new DataSet();
						PublishToWebWriterProvider.ChangeColumnDateTimeModeToUtc(dataAdapter, dataSet, tableDataWrapper.TableName);
						int i = dataAdapter.Fill(dataSet, num, num2, tableDataWrapper.TableName);
						ulong dataSetSizeWhenSerialized = PublishToWebWriterProvider.GetDataSetSizeWhenSerialized(dataSet, dataSetSerializer);
						PublishToWebWriterProvider.ComputingRowCountState computingRowCountState = PublishToWebWriterProvider.ComputingRowCountState.IncreasingRows;
						while (i > 0)
						{
							switch (computingRowCountState)
							{
							case PublishToWebWriterProvider.ComputingRowCountState.IncreasingRows:
								if (this.IsSizeAllowed(dataSetSizeWhenSerialized))
								{
									if (i < num2 || dataSetSizeWhenSerialized > 10485760uL)
									{
										computingRowCountState = PublishToWebWriterProvider.ComputingRowCountState.RowsFound;
									}
									else
									{
										num2 *= 10;
									}
								}
								else
								{
									if (num2 > 1)
									{
										num2 /= 10;
									}
									computingRowCountState = PublishToWebWriterProvider.ComputingRowCountState.DecreasingRows;
								}
								break;
							case PublishToWebWriterProvider.ComputingRowCountState.DecreasingRows:
								if (this.IsSizeAllowed(dataSetSizeWhenSerialized))
								{
									stopwatch2.Stop();
									stopwatch2.Reset();
									computingRowCountState = PublishToWebWriterProvider.ComputingRowCountState.RowsFound;
								}
								else
								{
									if (i == 1)
									{
										throw new PublishingServiceException(string.Format(CultureInfo.CurrentCulture, ScriptingEngineResources.ERROR_RowExceedsMaxRequestSize, new object[]
										{
											num + 1,
											tableDataWrapper.TableName
										}));
									}
									num2 /= 10;
								}
								break;
							case PublishToWebWriterProvider.ComputingRowCountState.RowsFound:
								if (this.IsSizeAllowed(dataSetSizeWhenSerialized))
								{
									this.PublishData(dataSet, tableDataWrapper.TableName);
									num += i;
								}
								else
								{
									stopwatch2.Start();
									num2 = 1;
									computingRowCountState = PublishToWebWriterProvider.ComputingRowCountState.IncreasingRows;
								}
								break;
							}
							dataSet.Dispose();
							dataSet = new DataSet();
							PublishToWebWriterProvider.ChangeColumnDateTimeModeToUtc(dataAdapter, dataSet, tableDataWrapper.TableName);
							dataSet.Locale = CultureInfo.InvariantCulture;
							i = dataAdapter.Fill(dataSet, num, num2, tableDataWrapper.TableName);
							dataSetSizeWhenSerialized = PublishToWebWriterProvider.GetDataSetSizeWhenSerialized(dataSet, dataSetSerializer);
						}
					}
					stopwatch.Stop();
				}
			}
			catch (Exception)
			{
				this._publishingState = PublishToWebWriterProvider.PublishingState.Error;
				throw;
			}
		}

		private static void ChangeColumnDateTimeModeToUtc(SqlDataAdapter sqlDataAdapter, DataSet dataSet, string tableName)
		{
			sqlDataAdapter.FillSchema(dataSet, SchemaType.Mapped);
			foreach (DataColumn dataColumn in dataSet.Tables[0].Columns)
			{
				if (dataColumn.DataType == typeof(DateTime))
				{
					dataColumn.DateTimeMode = DataSetDateTime.Utc;
				}
			}
			dataSet.Tables[0].TableName = tableName;
		}

		private static ulong GetDataSetSizeWhenSerialized(DataSet dataSet, XmlSerializer dataSetSerializer)
		{
			ulong result;
			using (MemoryStream memoryStream = new MemoryStream())
			{
				dataSetSerializer.Serialize(memoryStream, dataSet);
				long position = memoryStream.Position;
				result = (ulong)position;
			}
			return result;
		}

		private void PublishData(DataSet dataSet, string tableName)
		{
			try
			{
				this._webService.PublishData(dataSet);
			}
			catch (Exception innerException)
			{
				throw new PublishingServiceException(string.Format(CultureInfo.CurrentCulture, ScriptingEngineResources.ERROR_PublishDataInternalError, new object[]
				{
					tableName
				}), innerException);
			}
		}

		private void PublishScript(string script)
		{
			try
			{
				this._webService.PublishScript(script);
			}
			catch (Exception innerException)
			{
				throw new PublishingServiceException(string.Format(CultureInfo.CurrentCulture, ScriptingEngineResources.ERROR_PublishScriptFailed, new object[]
				{
					script
				}), innerException);
			}
		}

		private bool IsSizeAllowed(ulong size)
		{
			return size / 1024uL < this._webServiceOptions.MaxRequestLength;
		}
	}
}
